﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
using HostServicePhoneCommands.Properties;
using Microsoft.Win32.SafeHandles;

namespace HostServicePhoneCommands
{
    public static class ConsoleUtil
    {
        [DllImport("kernel32.dll", EntryPoint = "AllocConsole", SetLastError = true, CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        static extern bool AllocConsole();

        [DllImport("kernel32.dll", EntryPoint = "FreeConsole", SetLastError = true, CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        static extern bool FreeConsole();

        [DllImport("kernel32.dll",EntryPoint = "GetStdHandle",SetLastError = true,CharSet = CharSet.Auto,CallingConvention = CallingConvention.StdCall)]
        private static extern IntPtr GetStdHandle(int nStdHandle);

        [DllImport("kernel32.dll", EntryPoint = "SetConsoleTextAttribute",SetLastError = true, CharSet = CharSet.Auto,CallingConvention = CallingConvention.StdCall)]
        private static extern int SetConsoleTextAttribute(int hConsoleOutput,int wAttributes); 

        private const int StdOutputHandle = -11;
        private static int _hConsoleHandle;
        private static Thread _threadLogging;
        private static readonly Queue<LoggingMessage> QueueLog = new Queue<LoggingMessage>();
        private static ManualResetEvent _endThreadLog;

        public static  void SetColor(int color)
        {
            SetConsoleTextAttribute(_hConsoleHandle, color);
        }

        public static bool ModeConsoneOn { get; private set; }

        public static void AddLogDebug(string message)
        {
            if (!ModeConsoneOn) return;
            QueueLog.Enqueue(new LoggingMessage{ Message = message, ShowDate = true, Type = LoggingType.Debug  });
        }

        public static void AddLogError(string message)
        {
            if (!ModeConsoneOn) return;
            QueueLog.Enqueue(new LoggingMessage { Message = message, ShowDate = true, Type = LoggingType.Error });
        }

        public static void AddLog(LoggingMessage message)
        {
            if (!ModeConsoneOn) return;
            QueueLog.Enqueue(message);
        }

        public static void OpenConsole()
        {
            if (!ModeConsoneOn)
            {
                ModeConsoneOn = AllocConsole();
                if (ModeConsoneOn)
                {
                    _hConsoleHandle = (int) GetStdHandle(StdOutputHandle);

                    var safeFileHandle = new SafeFileHandle((IntPtr) _hConsoleHandle, true);
                    var fileStream = new FileStream(safeFileHandle, FileAccess.Write);
                    var standardOutput = new StreamWriter(fileStream) {AutoFlush = true};
                    Console.SetOut(standardOutput); 

                    
                    WriteInfo("===========================================================================", false);
                    WriteInfo(" " + Resources.TileConsole, false);
                    WriteWarnning(" " + Resources.ConsoleNotClose, false);
                    WriteInfo("===========================================================================", false);
                    WriteInfo("", false);

                    _endThreadLog = new ManualResetEvent(false);
                    _threadLogging = new Thread(ConsoleLogging);
                    _threadLogging.Start(null);

                }
            }
        }

        private static void ConsoleLogging(object param)
        {
            int timeout = 0;
            while (!_endThreadLog.WaitOne(timeout, false))
            {
                LoggingMessage task = null;
                if (QueueLog.Count > 0)
                {
                    task = QueueLog.Dequeue();
                }
                if (task != null)
                {
                    switch (task.Type)
                    {
                        case LoggingType.Info:
                            WriteInfo(task.Message, task.ShowDate);
                            break;
                        case LoggingType.Debug:
                            WriteDebug(task.Message, task.ShowDate);
                            break;
                        case LoggingType.Warnning:
                            WriteWarnning(task.Message, task.ShowDate);
                            break;
                        case LoggingType.Error:
                            WriteError(task.Message, task.ShowDate);
                            break;
                        default:
                            throw new ArgumentOutOfRangeException();
                    }
                    timeout = 0;
                }
                else
                {
                    timeout = 500;
                }
            }
        }

        public static void CloseConsole()
        {
            if (ModeConsoneOn)
            {
                QueueLog.Clear();
                _endThreadLog.Set();
                while (_threadLogging.ThreadState == ThreadState.Running)
                {
                    Thread.Sleep(100);
                }
                ModeConsoneOn = !FreeConsole();
            }
        }

        private static void WriteInfo(string value, bool dateTime)
        {
            SetColor((int)ConsoleColor.White);
            if (dateTime)
            {
                // ReSharper disable LocalizableElement
                Console.WriteLine("[{0}] {1}", DateTime.Now.ToLongTimeString(), value);
                // ReSharper restore LocalizableElement
            }
            else
            {
                Console.WriteLine(value);
            }
        }

        private static void WriteDebug(string value, bool dateTime)
        {
            SetColor((int)ConsoleColor.Green);
            if (dateTime)
            {
                // ReSharper disable LocalizableElement
                Console.WriteLine("[{0}] {1}", DateTime.Now.ToLongTimeString(), value);
                // ReSharper restore LocalizableElement
            }
            else
            {
                Console.WriteLine(value);
            }
        }

        private static void WriteWarnning(string value, bool dateTime)
        {
            SetColor((int)ConsoleColor.Yellow);
            if (dateTime)
            {
                // ReSharper disable LocalizableElement
                Console.WriteLine("[{0}] {1}", DateTime.Now.ToLongTimeString(), value);
                // ReSharper restore LocalizableElement
            }
            else
            {
                Console.WriteLine(value);
            }
        }

        private static void WriteError(string value, bool dateTime)
        {
            SetColor((int)ConsoleColor.Red);
            if (dateTime)
            {
                // ReSharper disable LocalizableElement
                Console.WriteLine("[{0}] {1}", DateTime.Now.ToLongTimeString(), value);
                // ReSharper restore LocalizableElement
            }
            else
            {
                Console.WriteLine(value);
            }
        }
    }

}
